{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "32f568fc",
   "metadata": {},
   "source": [
    "一个简单的示例，其中两个 agent 从 10 倒数到 1。\n",
    "\n",
    "我们首先定义 agent 类及其各自的处理消息的程序。 \n",
    "\n",
    "我们创建两个 agent 类：Modifier 和 Checker。\n",
    "\n",
    "Modifier agent 修改给定的数字，而 Check agent 根据条件检查该值。 \n",
    "\n",
    "我们还创建了一个 Message 数据类，它定义了在 agent 之间传递的消息。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "c3c5c6d0",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dataclasses import dataclass\n",
    "from typing import Callable\n",
    "\n",
    "from autogen_core import DefaultTopicId, MessageContext, RoutedAgent, default_subscription, message_handler\n",
    "\n",
    "\n",
    "@dataclass\n",
    "class Message:\n",
    "    content: int\n",
    "\n",
    "\n",
    "@default_subscription\n",
    "class Modifier(RoutedAgent):\n",
    "    def __init__(self, modify_val: Callable[[int], int]) -> None:\n",
    "        super().__init__(\"A modifier agent.\")\n",
    "        self._modify_val = modify_val\n",
    "\n",
    "    @message_handler\n",
    "    async def handle_message(self, message: Message, ctx: MessageContext) -> None:\n",
    "        val = self._modify_val(message.content)\n",
    "        print(f\"{'-'*80}\\nModifier:\\nModified {message.content} to {val}\")\n",
    "        await self.publish_message(Message(content=val), DefaultTopicId())  # type: ignore\n",
    "\n",
    "\n",
    "@default_subscription\n",
    "class Checker(RoutedAgent):\n",
    "    def __init__(self, run_until: Callable[[int], bool]) -> None:\n",
    "        super().__init__(\"A checker agent.\")\n",
    "        self._run_until = run_until\n",
    "\n",
    "    @message_handler\n",
    "    async def handle_message(self, message: Message, ctx: MessageContext) -> None:\n",
    "        if not self._run_until(message.content):\n",
    "            print(f\"{'-'*80}\\nChecker:\\n{message.content} passed the check, continue.\")\n",
    "            await self.publish_message(Message(content=message.content), DefaultTopicId())\n",
    "        else:\n",
    "            print(f\"{'-'*80}\\nChecker:\\n{message.content} failed the check, stopping.\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ec134bce",
   "metadata": {},
   "source": [
    "您可能已经注意到，agent 的逻辑（无论是使用模型还是代码执行器）与消息的传递方式完全分离。 \n",
    "\n",
    "这是核心思想：框架提供通信基础设施，agent 负责自己的逻辑。 我们将通信基础设施称为 **Agent 运行时**。\n",
    "\n",
    "Agent 运行时是此框架的关键概念。 除了传递消息之外，它还管理 agent 的生命周期。 因此，agent 的创建由运行时处理。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "f40779ac",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------------------------------------------------------------------\n",
      "Checker:\n",
      "10 passed the check, continue.\n",
      "--------------------------------------------------------------------------------\n",
      "Modifier:\n",
      "Modified 10 to 9\n",
      "--------------------------------------------------------------------------------\n",
      "Checker:\n",
      "9 passed the check, continue.\n",
      "--------------------------------------------------------------------------------\n",
      "Modifier:\n",
      "Modified 9 to 8\n",
      "--------------------------------------------------------------------------------\n",
      "Checker:\n",
      "8 passed the check, continue.\n",
      "--------------------------------------------------------------------------------\n",
      "Modifier:\n",
      "Modified 8 to 7\n",
      "--------------------------------------------------------------------------------\n",
      "Checker:\n",
      "7 passed the check, continue.\n",
      "--------------------------------------------------------------------------------\n",
      "Modifier:\n",
      "Modified 7 to 6\n",
      "--------------------------------------------------------------------------------\n",
      "Checker:\n",
      "6 passed the check, continue.\n",
      "--------------------------------------------------------------------------------\n",
      "Modifier:\n",
      "Modified 6 to 5\n",
      "--------------------------------------------------------------------------------\n",
      "Checker:\n",
      "5 passed the check, continue.\n",
      "--------------------------------------------------------------------------------\n",
      "Modifier:\n",
      "Modified 5 to 4\n",
      "--------------------------------------------------------------------------------\n",
      "Checker:\n",
      "4 passed the check, continue.\n",
      "--------------------------------------------------------------------------------\n",
      "Modifier:\n",
      "Modified 4 to 3\n",
      "--------------------------------------------------------------------------------\n",
      "Checker:\n",
      "3 passed the check, continue.\n",
      "--------------------------------------------------------------------------------\n",
      "Modifier:\n",
      "Modified 3 to 2\n",
      "--------------------------------------------------------------------------------\n",
      "Checker:\n",
      "2 passed the check, continue.\n",
      "--------------------------------------------------------------------------------\n",
      "Modifier:\n",
      "Modified 2 to 1\n",
      "--------------------------------------------------------------------------------\n",
      "Checker:\n",
      "1 failed the check, stopping.\n"
     ]
    }
   ],
   "source": [
    "from autogen_core import AgentId, SingleThreadedAgentRuntime\n",
    "\n",
    "# Create a local embedded runtime.\n",
    "runtime = SingleThreadedAgentRuntime()\n",
    "\n",
    "# Register the modifier and checker agents by providing\n",
    "# their agent types, the factory functions for creating instance and subscriptions.\n",
    "await Modifier.register(\n",
    "    runtime,\n",
    "    \"modifier\",\n",
    "    # Modify the value by subtracting 1\n",
    "    lambda: Modifier(modify_val=lambda x: x - 1),\n",
    ")\n",
    "\n",
    "await Checker.register(\n",
    "    runtime,\n",
    "    \"checker\",\n",
    "    # Run until the value is less than or equal to 1\n",
    "    lambda: Checker(run_until=lambda x: x <= 1),\n",
    ")\n",
    "\n",
    "# Start the runtime and send a direct message to the checker.\n",
    "runtime.start()\n",
    "await runtime.send_message(Message(10), AgentId(\"checker\", \"default\"))\n",
    "await runtime.stop_when_idle()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "617130a9",
   "metadata": {},
   "source": [
    "从 agent 的输出中，我们可以看到该值已成功从 10 递减到 1，正如 modifier 和 checker 条件所规定。\n",
    "\n",
    "AutoGen 还支持分布式 agent 运行时，它可以托管在不同进程或机器上运行的具有不同身份、语言和依赖项的 agent。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6fb5716e",
   "metadata": {},
   "source": [
    "https://msdocs.cn/autogen/stable/user-guide/core-user-guide/design-patterns/intro.html\n",
    "\n",
    "autogen 多代理模式的几个典型应用场景： 工作流、群聊、辩论、反思"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "0dc34a75",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Collecting autogen_ext\n",
      "  Downloading autogen_ext-0.7.4-py3-none-any.whl.metadata (7.3 kB)\n",
      "Requirement already satisfied: autogen-core==0.7.4 in d:\\software\\miniconda3\\envs\\mlops\\lib\\site-packages (from autogen_ext) (0.7.4)\n",
      "Requirement already satisfied: jsonref~=1.1.0 in d:\\software\\miniconda3\\envs\\mlops\\lib\\site-packages (from autogen-core==0.7.4->autogen_ext) (1.1.0)\n",
      "Requirement already satisfied: opentelemetry-api>=1.34.1 in d:\\software\\miniconda3\\envs\\mlops\\lib\\site-packages (from autogen-core==0.7.4->autogen_ext) (1.36.0)\n",
      "Collecting pillow>=11.0.0 (from autogen-core==0.7.4->autogen_ext)\n",
      "  Using cached pillow-11.3.0-cp312-cp312-win_amd64.whl.metadata (9.2 kB)\n",
      "Requirement already satisfied: protobuf~=5.29.3 in d:\\software\\miniconda3\\envs\\mlops\\lib\\site-packages (from autogen-core==0.7.4->autogen_ext) (5.29.5)\n",
      "Requirement already satisfied: pydantic<3.0.0,>=2.10.0 in d:\\software\\miniconda3\\envs\\mlops\\lib\\site-packages (from autogen-core==0.7.4->autogen_ext) (2.11.7)\n",
      "Requirement already satisfied: typing-extensions>=4.0.0 in d:\\software\\miniconda3\\envs\\mlops\\lib\\site-packages (from autogen-core==0.7.4->autogen_ext) (4.15.0)\n",
      "Requirement already satisfied: annotated-types>=0.6.0 in d:\\software\\miniconda3\\envs\\mlops\\lib\\site-packages (from pydantic<3.0.0,>=2.10.0->autogen-core==0.7.4->autogen_ext) (0.7.0)\n",
      "Requirement already satisfied: pydantic-core==2.33.2 in d:\\software\\miniconda3\\envs\\mlops\\lib\\site-packages (from pydantic<3.0.0,>=2.10.0->autogen-core==0.7.4->autogen_ext) (2.33.2)\n",
      "Requirement already satisfied: typing-inspection>=0.4.0 in d:\\software\\miniconda3\\envs\\mlops\\lib\\site-packages (from pydantic<3.0.0,>=2.10.0->autogen-core==0.7.4->autogen_ext) (0.4.1)\n",
      "Requirement already satisfied: importlib-metadata<8.8.0,>=6.0 in d:\\software\\miniconda3\\envs\\mlops\\lib\\site-packages (from opentelemetry-api>=1.34.1->autogen-core==0.7.4->autogen_ext) (8.7.0)\n",
      "Requirement already satisfied: zipp>=3.20 in d:\\software\\miniconda3\\envs\\mlops\\lib\\site-packages (from importlib-metadata<8.8.0,>=6.0->opentelemetry-api>=1.34.1->autogen-core==0.7.4->autogen_ext) (3.23.0)\n",
      "Downloading autogen_ext-0.7.4-py3-none-any.whl (328 kB)\n",
      "Using cached pillow-11.3.0-cp312-cp312-win_amd64.whl (7.0 MB)\n",
      "Installing collected packages: pillow, autogen_ext\n",
      "\n",
      "  Attempting uninstall: pillow\n",
      "\n",
      "    Found existing installation: pillow 10.4.0\n",
      "\n",
      "    Uninstalling pillow-10.4.0:\n",
      "\n",
      "      Successfully uninstalled pillow-10.4.0\n",
      "\n",
      "   ---------------------------------------- 0/2 [pillow]\n",
      "   ---------------------------------------- 0/2 [pillow]\n",
      "   ---------------------------------------- 0/2 [pillow]\n",
      "   ---------------------------------------- 0/2 [pillow]\n",
      "   ---------------------------------------- 0/2 [pillow]\n",
      "   ---------------------------------------- 0/2 [pillow]\n",
      "   ---------------------------------------- 0/2 [pillow]\n",
      "   ---------------------------------------- 0/2 [pillow]\n",
      "   ---------------------------------------- 0/2 [pillow]\n",
      "   ---------------------------------------- 0/2 [pillow]\n",
      "   ---------------------------------------- 0/2 [pillow]\n",
      "   ---------------------------------------- 0/2 [pillow]\n",
      "   ---------------------------------------- 0/2 [pillow]\n",
      "   ---------------------------------------- 0/2 [pillow]\n",
      "   ---------------------------------------- 0/2 [pillow]\n",
      "   ---------------------------------------- 0/2 [pillow]\n",
      "   ---------------------------------------- 0/2 [pillow]\n",
      "   ---------------------------------------- 0/2 [pillow]\n",
      "   ---------------------------------------- 0/2 [pillow]\n",
      "   -------------------- ------------------- 1/2 [autogen_ext]\n",
      "   -------------------- ------------------- 1/2 [autogen_ext]\n",
      "   -------------------- ------------------- 1/2 [autogen_ext]\n",
      "   ---------------------------------------- 2/2 [autogen_ext]\n",
      "\n",
      "Successfully installed autogen_ext-0.7.4 pillow-11.3.0\n",
      "Note: you may need to restart the kernel to use updated packages.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ProxyError('Cannot connect to proxy.', TimeoutError('_ssl.c:993: The handshake operation timed out'))': /simple/autogen-ext/\n",
      "  WARNING: Failed to remove contents in a temporary directory 'D:\\software\\miniconda3\\envs\\MLOps\\Lib\\site-packages\\~il'.\n",
      "  You can safely remove it manually.\n",
      "ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n",
      "langchain-nomic 0.1.4 requires pillow<11.0.0,>=10.3.0, but you have pillow 11.3.0 which is incompatible.\n",
      "llama-index-embeddings-dashscope 0.1.4 requires llama-index-core<0.11.0,>=0.10.0, but you have llama-index-core 0.13.4 which is incompatible.\n",
      "llama-index-postprocessor-dashscope-rerank-custom 0.1.0 requires llama-index-core<0.11.0,>=0.10.0, but you have llama-index-core 0.13.4 which is incompatible.\n",
      "llama-index-vector-stores-faiss 0.1.2 requires llama-index-core<0.11.0,>=0.10.1, but you have llama-index-core 0.13.4 which is incompatible.\n"
     ]
    }
   ],
   "source": [
    "%pip install autogen_ext"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "07f4d18e",
   "metadata": {},
   "source": [
    "## 顺序工作流\n",
    "\n",
    "顺序工作流是一种多 Agent 设计模式，其中 Agent 以确定性的顺序响应。 工作流中的每个 Agent 通过处理消息、生成响应，然后将其传递给下一个 Agent 来执行特定任务。 此模式对于创建确定性工作流非常有用，其中每个 Agent 都有助于预先指定的子任务。\n",
    "\n",
    "在此示例中，我们将演示一个顺序工作流，其中多个 Agent 协同工作，将基本产品描述转换为精美的营销文案。\n",
    "\n",
    "该管道由四个专业的 Agent 组成\n",
    "\n",
    "- 概念提取 Agent：分析初始产品描述以提取关键功能、目标受众和独特的卖点 (USP)。 输出是单个文本块中的结构化分析。\n",
    "\n",
    "- 编写 Agent：根据提取的概念制作引人注目的营销文案。 此 Agent 将分析见解转化为引人入胜的促销内容，并在单个文本块中提供连贯的叙述。\n",
    "\n",
    "- 格式和校对 Agent：通过改进语法、增强清晰度和保持一致的语气来润色草稿副本。 此 Agent 确保专业质量并提供格式良好的最终版本。\n",
    "\n",
    "-用户 Agent：向用户展示最终的、精美的营销文案，完成工作流程。\n",
    "\n",
    "下图说明了此示例中的顺序工作流程\n",
    "\n",
    "![img](https://msdocs.cn/autogen/stable/_images/sequential-workflow.svg)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "721c92b8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------------------------------------------------------------------\n",
      "ConceptExtractorAgent:\n",
      "**Key Features:**\n",
      "- Made from eco-friendly stainless steel\n",
      "- Insulation technology that keeps drinks cold for up to 24 hours\n",
      "- Reusable, reducing waste from disposable plastic bottles\n",
      "- Durable design, resistant to rust and corrosion\n",
      "- Available in multiple colors and sizes\n",
      "\n",
      "**Target Audience:**\n",
      "- Environmentally conscious consumers\n",
      "- Health and fitness enthusiasts (those who enjoy outdoor activities, gym-goers)\n",
      "- Individuals looking for sustainable alternatives to plastic products\n",
      "- Students and professionals who carry beverages throughout the day\n",
      "\n",
      "**Unique Selling Points:**\n",
      "- Eco-friendly materials that appeal to environmentally responsible shoppers\n",
      "- Exceptional insulation performance, ensuring cold beverages last all day\n",
      "- Reusability contributes to reducing plastic waste, aligning with sustainability goals\n",
      "- Stylish design options cater to personal aesthetics while promoting a green lifestyle\n",
      "--------------------------------------------------------------------------------\n",
      "WriterAgent:\n",
      "Discover the perfect blend of style and sustainability with our eco-friendly stainless steel water bottles! Designed for the environmentally conscious and health enthusiasts alike, our bottles utilize advanced insulation technology to keep your drinks cold for an impressive 24 hours. Say goodbye to single-use plastics and embrace a durable, reusable alternative that not only helps the planet but also complements your active lifestyle.\n",
      "\n",
      "Choose from a vibrant array of colors and sizes to match your personal aesthetic while reducing waste. Whether you’re hitting the gym, heading to class, or embarking on an outdoor adventure, our stainless steel bottles are built to withstand rust and corrosion, ensuring they stay as good as new. Join the movement towards a greener future—hydrate sustainably and stylishly today!\n",
      "--------------------------------------------------------------------------------\n",
      "FormatProofAgent:\n",
      "Discover the perfect blend of style and sustainability with our eco-friendly stainless steel water bottles! Designed for environmentally conscious individuals and health enthusiasts alike, our bottles feature advanced insulation technology that keeps your beverages cold for an impressive 24 hours. Say goodbye to single-use plastics and embrace a durable, reusable alternative that not only helps the planet but also fits seamlessly into your active lifestyle.\n",
      "\n",
      "Choose from a vibrant array of colors and sizes to match your personal aesthetic while reducing waste. Whether you’re hitting the gym, heading to class, or embarking on an outdoor adventure, our stainless steel bottles are built to withstand rust and corrosion, ensuring they remain as good as new. Join the movement toward a greener future—hydrate sustainably and stylishly today!\n",
      "\n",
      "--------------------------------------------------------------------------------\n",
      "User received final copy:\n",
      "Discover the perfect blend of style and sustainability with our eco-friendly stainless steel water bottles! Designed for environmentally conscious individuals and health enthusiasts alike, our bottles feature advanced insulation technology that keeps your beverages cold for an impressive 24 hours. Say goodbye to single-use plastics and embrace a durable, reusable alternative that not only helps the planet but also fits seamlessly into your active lifestyle.\n",
      "\n",
      "Choose from a vibrant array of colors and sizes to match your personal aesthetic while reducing waste. Whether you’re hitting the gym, heading to class, or embarking on an outdoor adventure, our stainless steel bottles are built to withstand rust and corrosion, ensuring they remain as good as new. Join the movement toward a greener future—hydrate sustainably and stylishly today!\n"
     ]
    }
   ],
   "source": [
    "\n",
    "\n",
    "from dataclasses import dataclass\n",
    "\n",
    "from autogen_core import (\n",
    "    MessageContext,\n",
    "    RoutedAgent,\n",
    "    SingleThreadedAgentRuntime,\n",
    "    TopicId,\n",
    "    TypeSubscription,\n",
    "    message_handler,\n",
    "    type_subscription,\n",
    ")\n",
    "from autogen_core.models import ChatCompletionClient, SystemMessage, UserMessage\n",
    "from autogen_ext.models.openai import OpenAIChatCompletionClient\n",
    "\n",
    "# Agent 将使用该消息来传递他们的工作\n",
    "@dataclass\n",
    "class Message:\n",
    "    content: str\n",
    "\n",
    "# 工作流程中的每个 Agent 都将订阅特定的主题类型。 \n",
    "# 主题类型以序列中 Agent 的名称命名， 这允许每个 Agent 将其工作发布到序列中的下一个 Agent。\n",
    "\n",
    "concept_extractor_topic_type = \"ConceptExtractorAgent\"\n",
    "writer_topic_type = \"WriterAgent\"\n",
    "format_proof_topic_type = \"FormatProofAgent\"\n",
    "user_topic_type = \"User\"\n",
    "\n",
    "\n",
    "# 每个 Agent 类都使用 type_subscription 装饰器来指定它订阅的主题类型。 \n",
    "# 除了装饰器，您还可以使用 add_subscription() 方法直接通过运行时订阅主题。\n",
    "\n",
    "# \"概念提取 Agent \"提出了产品描述的初始要点。\n",
    "@type_subscription(topic_type=concept_extractor_topic_type)\n",
    "class ConceptExtractorAgent(RoutedAgent):\n",
    "    def __init__(self, model_client: ChatCompletionClient) -> None:\n",
    "        super().__init__(\"A concept extractor agent.\")\n",
    "        self._system_message = SystemMessage(\n",
    "            content=(\n",
    "                \"You are a marketing analyst. Given a product description, identify:\\n\"\n",
    "                \"- Key features\\n\"\n",
    "                \"- Target audience\\n\"\n",
    "                \"- Unique selling points\\n\\n\"\n",
    "            )\n",
    "        )\n",
    "        self._model_client = model_client\n",
    "\n",
    "    @message_handler\n",
    "    async def handle_user_description(self, message: Message, ctx: MessageContext) -> None:\n",
    "        prompt = f\"Product description: {message.content}\"\n",
    "        llm_result = await self._model_client.create(\n",
    "            messages=[self._system_message, UserMessage(content=prompt, source=self.id.key)],\n",
    "            cancellation_token=ctx.cancellation_token,\n",
    "        )\n",
    "        response = llm_result.content\n",
    "        assert isinstance(response, str)\n",
    "        print(f\"{'-'*80}\\n{self.id.type}:\\n{response}\")\n",
    "\n",
    "        await self.publish_message(Message(response), topic_id=TopicId(writer_topic_type, source=self.id.key))\n",
    "\n",
    "# \"编写 Agent \"执行编写\n",
    "@type_subscription(topic_type=writer_topic_type)\n",
    "class WriterAgent(RoutedAgent):\n",
    "    def __init__(self, model_client: ChatCompletionClient) -> None:\n",
    "        super().__init__(\"A writer agent.\")\n",
    "        self._system_message = SystemMessage(\n",
    "            content=(\n",
    "                \"You are a marketing copywriter. Given a block of text describing features, audience, and USPs, \"\n",
    "                \"compose a compelling marketing copy (like a newsletter section) that highlights these points. \"\n",
    "                \"Output should be short (around 150 words), output just the copy as a single text block.\"\n",
    "            )\n",
    "        )\n",
    "        self._model_client = model_client\n",
    "\n",
    "    @message_handler\n",
    "    async def handle_intermediate_text(self, message: Message, ctx: MessageContext) -> None:\n",
    "        prompt = f\"Below is the info about the product:\\n\\n{message.content}\"\n",
    "\n",
    "        llm_result = await self._model_client.create(\n",
    "            messages=[self._system_message, UserMessage(content=prompt, source=self.id.key)],\n",
    "            cancellation_token=ctx.cancellation_token,\n",
    "        )\n",
    "        response = llm_result.content\n",
    "        assert isinstance(response, str)\n",
    "        print(f\"{'-'*80}\\n{self.id.type}:\\n{response}\")\n",
    "\n",
    "        await self.publish_message(Message(response), topic_id=TopicId(format_proof_topic_type, source=self.id.key))\n",
    "\n",
    "# \"格式校对 Agent \"执行格式化。\n",
    "@type_subscription(topic_type=format_proof_topic_type)\n",
    "class FormatProofAgent(RoutedAgent):\n",
    "    def __init__(self, model_client: ChatCompletionClient) -> None:\n",
    "        super().__init__(\"A format & proof agent.\")\n",
    "        self._system_message = SystemMessage(\n",
    "            content=(\n",
    "                \"You are an editor. Given the draft copy, correct grammar, improve clarity, ensure consistent tone, \"\n",
    "                \"give format and make it polished. Output the final improved copy as a single text block.\"\n",
    "            )\n",
    "        )\n",
    "        self._model_client = model_client\n",
    "\n",
    "    @message_handler\n",
    "    async def handle_intermediate_text(self, message: Message, ctx: MessageContext) -> None:\n",
    "        prompt = f\"Draft copy:\\n{message.content}.\"\n",
    "        llm_result = await self._model_client.create(\n",
    "            messages=[self._system_message, UserMessage(content=prompt, source=self.id.key)],\n",
    "            cancellation_token=ctx.cancellation_token,\n",
    "        )\n",
    "        response = llm_result.content\n",
    "        assert isinstance(response, str)\n",
    "        print(f\"{'-'*80}\\n{self.id.type}:\\n{response}\")\n",
    "\n",
    "        await self.publish_message(Message(response), topic_id=TopicId(user_topic_type, source=self.id.key))\n",
    "\n",
    "# \"用户 Agent \"只是将最终的营销文案打印到控制台。 \n",
    "# 在实际应用中，这可以替换为将结果存储到数据库、发送电子邮件或任何其他所需的操作。\n",
    "@type_subscription(topic_type=user_topic_type)\n",
    "class UserAgent(RoutedAgent):\n",
    "    def __init__(self) -> None:\n",
    "        super().__init__(\"A user agent that outputs the final copy to the user.\")\n",
    "\n",
    "    @message_handler\n",
    "    async def handle_final_copy(self, message: Message, ctx: MessageContext) -> None:\n",
    "        print(f\"\\n{'-'*80}\\n{self.id.type} received final copy:\\n{message.content}\")\n",
    "\n",
    "\n",
    "\n",
    "# 将 Agent 注册到运行时。 \n",
    "# 因为我们使用了 type_subscription 装饰器，所以运行时会自动将 Agent 订阅到正确的主题。\n",
    "model_client = OpenAIChatCompletionClient(\n",
    "    model=\"gpt-4o-mini\",\n",
    "    # api_key=\"YOUR_API_KEY\"\n",
    ")\n",
    "\n",
    "runtime = SingleThreadedAgentRuntime()\n",
    "\n",
    "await ConceptExtractorAgent.register(\n",
    "    runtime, type=concept_extractor_topic_type, factory=lambda: ConceptExtractorAgent(model_client=model_client)\n",
    ")\n",
    "\n",
    "await WriterAgent.register(runtime, type=writer_topic_type, factory=lambda: WriterAgent(model_client=model_client))\n",
    "\n",
    "await FormatProofAgent.register(\n",
    "    runtime, type=format_proof_topic_type, factory=lambda: FormatProofAgent(model_client=model_client)\n",
    ")\n",
    "\n",
    "await UserAgent.register(runtime, type=user_topic_type, factory=lambda: UserAgent())\n",
    "\n",
    "\n",
    "# 最后，我们可以通过将消息发布到序列中的第一个 Agent 来运行工作流程\n",
    "runtime.start()\n",
    "\n",
    "await runtime.publish_message(\n",
    "    Message(content=\"An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours\"),\n",
    "    topic_id=TopicId(concept_extractor_topic_type, source=\"default\"),\n",
    ")\n",
    "\n",
    "await runtime.stop_when_idle()\n",
    "await model_client.close()\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d9e24f23",
   "metadata": {},
   "source": [
    "## 群聊模式的消息协议\n",
    "\n",
    "首先，用户或外部 Agent 将 GroupChatMessage 消息发布到所有参与者的共同主题。\n",
    "\n",
    "群聊管理器选择下一个发言人，并向该 Agent 发送 RequestToSpeak 消息。\n",
    "\n",
    "Agent 在收到 RequestToSpeak 消息后，将 GroupChatMessage 消息发布到公共主题。\n",
    "\n",
    "此过程一直持续到群聊管理器达到终止条件，然后停止发出 RequestToSpeak 消息，并且群聊结束。\n",
    "\n",
    "\n",
    "![img](https://msdocs.cn/autogen/stable/_images/groupchat.svg)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f9614208",
   "metadata": {},
   "source": [
    "## 多智能体辩论\n",
    "\n",
    "多智能体辩论是一种多智能体设计模式，它模拟了多轮交互，在每一轮中，智能体之间交换响应，并根据其他智能体的响应改进其响应。\n",
    "\n",
    "\n",
    "此模式中有两种类型的智能体：求解器智能体和聚合器智能体。 \n",
    "\n",
    "求解器智能体以稀疏的方式连接，遵循 使用稀疏通信拓扑改进多智能体辩论 中描述的技术。 \n",
    "\n",
    "求解器智能体负责解决数学问题并相互交换响应。 \n",
    "\n",
    "聚合器智能体负责将数学问题分发给求解器智能体，等待他们的最终响应，并聚合这些响应以获得最终答案。\n",
    "\n",
    "\n",
    "**该模式的工作方式如下**\n",
    "\n",
    "1. 用户向聚合器智能体发送一个数学问题。\n",
    "\n",
    "2. 聚合器智能体将问题分发给求解器智能体。\n",
    "\n",
    "3. 每个求解器智能体处理该问题，并向其邻居发布响应。\n",
    "\n",
    "4. 每个求解器智能体使用来自其邻居的响应来改进其响应，并发布新的响应。\n",
    "\n",
    "5. 重复步骤 4 固定轮数。 在最后一轮中，每个求解器智能体发布最终响应。\n",
    "\n",
    "6. 聚合器智能体使用多数投票来聚合来自所有求解器智能体的最终响应以获得最终答案，并发布该答案。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c2dfc743",
   "metadata": {},
   "source": [
    "## 反思\n",
    "\n",
    "应用程序向编码器代理发送一个 CodeWritingTask 消息\n",
    "\n",
    "编码器代理生成一个 CodeReviewTask 消息，该消息被发送到审查器代理\n",
    "\n",
    "审查器代理生成一个 CodeReviewResult 消息，该消息被发送回编码器代理\n",
    "\n",
    "取决于 CodeReviewResult 消息，如果代码被批准，编码器代理将一个 CodeWritingResult 消息发送回应用程序，否则，编码器代理将另一个 CodeReviewTask 消息发送到审查器代理，并且该过程继续。\n",
    "\n",
    "![](https://msdocs.cn/autogen/stable/_images/coder-reviewer-data-flow.svg)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "MLOps",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
